//
//  APIComponent.swift
//  xxx
//
//  Created by linhan on 15/8/7.
//  Copyright (c) 2015年 xxx. All rights reserved.
//

import Foundation

enum HTTPMethod:String
{
    case GET
    case POST
}

protocol SWRequestDelegate:NSObjectProtocol
{
    //获取到响应数据时回调
    func request(_ request:SWBaseRequest, didFinishLoading data:Data, fromCache cache:Bool)
    func request(_ request:SWBaseRequest, didFinishLoading string:String, fromCache cache:Bool)
    func request(_ request:SWBaseRequest, APISuccess data:Any, fromCache cache:Bool)
    func request(_ request:SWBaseRequest, APIFailed error:NSError)
}

class SWBaseRequest: NSObject,WebLoaderDelegate
{
    weak var delegate:SWRequestDelegate?
    var successCallback:((Any, Bool)->Void)?
    var failCallback:((NSError)->Void)?
    
    //在主线程中进行回调
    var callbackOnMainThread:Bool = true
    
    var api:SWRequestAPI = .None
    
    var method:HTTPMethod = .POST
    
    var headers:[String : String]?
    
    //请求URL，默认基于基路径加API路径；如果子类请求URL不遵循此规则，可在子类中重写
    var url:String
    {
        return "\(API_BASE_PATH)\(api.rawValue)"
    }
    
    //请求参数，子类请求如果带参数，可各自在子类中重写构成
    var params:Any?
    {
        return nil
    }
    
    //
    var options:SWRequestOptions = SWRequestOptions()
    
    //跟随请求携带的自定义内容，可用于响应时
    var binds:AnyObject?
    private let webloader:WebLoader = WebLoader()
    private let KeyMaxRetryTimes:String = "KeyMaxRetryTimes"
    private let KeyRetriedTimes:String = "KeyRetriedTimes"
    
    private var _retryMap:[String:[String:Int]] = [:]
    
    override init() {
        super.init()
        webloader.delegate = self
    }
    
    
    func load()
    {
        _retryMap[url] = [KeyMaxRetryTimes:options.retryTimes, KeyRetriedTimes:0]
        
        var send:Bool = false
        //send = true
        if options.authorize
        {
            if let account = SWAccountManager.sharedManager().account,
               let uid = account.uid , uid != "",
               let token = account.token?.token
            {
                headers = headers ?? [:]
                headers?["ticket"] = headers?["ticket"] ?? token
                send = true
            }
            else
            {
                notifyAPIFailed(SWError.TokenInvalidError)
                SWNotificationCenter.post(name: NotificationName.NeedLogin)
            }
        }
        else
        {
            send = true
        }
        
        if send
        {
            sendRequest()
        }
    }
    
    private func sendRequest()
    {
        let AppVersion:String = AppConfig.shared().version
        var headers:[String : String] = self.headers ?? [:]
        headers["Accept-Language"] = "zh-hans"
        headers["X-Yiya-Platform"] = "1"
        headers["X-Yiya-Version"] = AppVersion
        headers["User-Agent"] = "yiya/\(AppVersion)(iPhone;iOS 8.1.2)"
        
        webloader.load(url, data: params, method: method.rawValue, headers: headers, returnCacheData: options.returnCacheData)
        trace("load:", url)
    }
    
    func cancel()
    {
        print("取消请求:", url)
        webloader.cancel()
    }
    
    func onAPISuccessWithJSON(_ json:JSON, JSONString:String, JSONData:Data, cache:Bool)
    {
        if let model = JSONToModel(json, JSONString:JSONString, JSONData:JSONData)
        {
            onAPISuccess(model, cache:cache)
        }
        else
        {
            onAPISuccess(json, cache:cache)
        }
    }
    
    func onAPISuccess(_ data:Any, cache:Bool)
    {
        if callbackOnMainThread
        {
            DispatchQueue.main.async{
                self.delegate?.request(self, APISuccess: data, fromCache: cache)
                self.successCallback?(data, cache)
            }
        }
        else {
            delegate?.request(self, APISuccess: data, fromCache: cache)
            successCallback?(data, cache)
        }
    }
    
    //需要对字符串进行具体分析，可在子类中重写
    func onFinishLoadingWithString(_ data:String, cache:Bool)
    {
        if callbackOnMainThread
        {
            DispatchQueue.main.async{
                self.delegate?.request(self, didFinishLoading: data, fromCache: cache)
            }
        }
        else
        {
            delegate?.request(self, didFinishLoading: data, fromCache: cache)
        }
    }
    
    //JSON -> Model,具体的模型化过程请在子类中重写
    func JSONToModel(_ json:JSON, JSONString:String, JSONData:Data) -> AnyObject?
    {
        return nil
    }
    
    func didFinishLoading(_ data:Data, cache:Bool)
    {
        if options.format == .JSON
        {
            let readingOptions = options.JSONReadingOptions
            let JSONString:String = String(data: data, encoding: String.Encoding.utf8) ?? ""
            var json:JSON = JSON(data:data, options:readingOptions)
            //trace("json:\(json)")]
            if !options.resultCodeCheck
            {
                onAPISuccessWithJSON(json, JSONString:JSONString, JSONData:data, cache:cache)
            }
            else if let resultCode = json["Statu"].string
            {
                if resultCode == "1"
                {
                    onAPISuccessWithJSON(json, JSONString:JSONString, JSONData:data, cache:cache)
                }
                else if resultCode == "-1"
                {
                    notifyAPIFailed(SWError.TokenInvalidError)
                    SWNotificationCenter.post(name: NotificationName.NeedLogin)
                }
                else
                {
                    let message:String = json["StatusReson"].stringValue
                    if !cache
                    {
                        onAPIFailed(NSError(domain: SWError.domain, code: resultCode.intValue, userInfo: [NSLocalizedDescriptionKey:message]))
                    }
                }
            }
            else
            {
                if !cache
                {
                    onAPIFailed(SWError.DataError)
                }
            }
        }
        else if options.format == .String
        {
            let dataString:String = String(data: data, encoding: String.Encoding.utf8) ?? ""
            onFinishLoadingWithString(dataString, cache: cache)
        }
        else if options.format == .Binary
        {
            if callbackOnMainThread
            {
                DispatchQueue.main.async{
                    self.delegate?.request(self, didFinishLoading:data, fromCache: cache)
                }
            }
            else
            {
                delegate?.request(self, didFinishLoading:data, fromCache: cache)
            }
        }
    }
    
    func webLoaderDidFinishLoading(_ webLoader:WebLoader, data:Data, bindArgs:Any?)
    {
        if data.length > 0
        {
            didFinishLoading(data, cache: false)
        }
        else
        {
            onAPIFailed(SWError.DataError)
        }
    }
    
    func webLoaderCacheDataDidFinishLoading(_ webLoader:WebLoader, data:Data, bindArgs:Any?)
    {
        if data.length > 0
        {
            didFinishLoading(data, cache: true)
        }
    }
    
    func webLoaderDidFail(_ webLoader:WebLoader, error:NSError?, bindArgs:Any?)
    {
        onAPIFailed(SWError.DefaultNetworkError)
    }
    
    func onAPIFailed(_ error:NSError)
    {
        let url:String = webloader.url
        if let maxRetryTimes = _retryMap[url]?[KeyMaxRetryTimes] , maxRetryTimes > 0
        {
            let retriedTimes:Int = _retryMap[url]?[KeyRetriedTimes] ?? 0
            if retriedTimes < maxRetryTimes
            {
                _retryMap[url]?[KeyRetriedTimes] = retriedTimes + 1
                sendRequest()
            }
            else
            {
                notifyAPIFailed(error)
            }
        }
        else
        {
            notifyAPIFailed(error)
        }
    }
    
    private func notifyAPIFailed(_ error:NSError)
    {
        if callbackOnMainThread
        {
            DispatchQueue.main.async{
                self.delegate?.request(self, APIFailed: error)
                self.failCallback?(error)
            }
        }
        else
        {
            delegate?.request(self, APIFailed: error)
            failCallback?(error)
        }
    }
    
}
